


var iMacros = ( function() {
    "use strict";
    const Cu = Components.utils;
    const Ci = Components.interfaces;
    const Cc = Components.classes;
    function require(url) {
        let scope = {};
        Cu.import(url, scope);
        return scope;
    }
    let {MiscUtils, StrUtils} = require("chrome://jsm/content/utils.js");
    let {Prefs} = require("chrome://jsm/content/Preferences.js");
    let {Clipboard} = require("chrome://jsm/content/Clipboard.js");
    let {Dialogs} = require("chrome://jsm/content/Dialogs.js");
    let {FIO} = require("chrome://jsm/content/FileHelpers.js");
    let {SOAPClient} = require("chrome://jsm/content/SOAPClient.js");
    let {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
    let {OS} = require("resource://gre/modules/osfile.jsm");

    let obj = {};

    
    obj.GUID = "{81BF1D23-5F17-408D-AC6B-BD6DF7CAF670}";

    Object.defineProperty(obj, "recorder", {
        get() {return obj.conv_recorder;}
    });

    Object.defineProperty(obj, "playing", {
        get() {return obj.player && obj.player.playing;}
    });

    Object.defineProperty(obj, "jsplaying", { 
        get() {return obj.jsplayer2 && obj.jsplayer2.playing;}
    });

    Object.defineProperty(obj, "recording", {
        get() {return obj.recorder && obj.recorder.recording;}
    });

    Object.defineProperty(obj, "paused", {
        get() {
            if (obj.recording && obj.recorder.isPaused())
                return "recording";
            else if (obj.playing && obj.player.isPaused())
                return "playing";
            else if (obj.jsplaying && obj.jsplayer2.isPaused())
                return "jsplaying";
            else
                return null;
        }
    });

    

    
    
    obj.addTab = function(url) {
        var browser = getBrowser();
        browser.selectedTab = browser.addTab(url);
    };
    


    obj.showPrefDialog = function(pane) {    
        var param = { pane: pane };
        window.openDialog('chrome://imacros/content/options.xul', '', 
                          "chrome,titlebar,toolbar,centerscreen,modal",
                          param);
    };


    
    obj.makeBookmarkletURL = function(name, code) {
        
        let pattern = "(function() {"+
            "try{"+
            "var e_m64 = \"{{macro}}\", n64 = \"{{name}}\";"+
            "if(!/^(?:chrome|https?|file)/.test(location)){"+
            "alert('iMacros: Open webpage to run a macro.');"+
            "return;"+
            "}"+
            "var macro = {};"+
            "macro.source = decodeURIComponent(atob(e_m64));"+
            "macro.name = decodeURIComponent(atob(n64));"+
            "var evt = document.createEvent(\"CustomEvent\");"+
            "evt.initCustomEvent(\"iMacrosRunMacro\", true, true, macro);"+
            "window.dispatchEvent(evt);"+
            "}catch(e){alert('iMacros Bookmarklet error: '+e.toString());}"+
            "}) ();";
        
        let macro_name = name || "Unnamed Macro", source = code;
        macro_name = btoa(encodeURIComponent(name));
        macro_name = StrUtils.escapeLine(macro_name);
        pattern = pattern.replace("{{name}}", macro_name);
        source = btoa(encodeURIComponent(source));
        source = StrUtils.escapeLine(source);
        pattern = pattern.replace("{{macro}}", source);
        let url = "javascript:" + pattern;

        return url;
    };


    obj.addBookmark = function(type) {
        let item = this.panel.selectedItem;
        FIO.readTextFile(FIO.getMacroPath(item.path)).then( data => {
            let jsurl = this.makeBookmarkletURL(OS.Path.basename(item.path),
                                                data);
            let code = encodeURIComponent(StrUtils.encodeBase64(data));
            
            let params = {
                title: OS.Path.basename(item.path),
                url: "imacros://run/?m="+encodeURIComponent(item.path),
                bookmarklet: jsurl,
                res: null,
                type: 1,
                folderId: -1,
                tags: ""
            };

            window.openDialog('chrome://imacros/content/bookmark.xul','',
                              'modal,dialog,centerscreen,resizable=yes',
                              params);
            if (params.res) {
                if (params.type == 1) {
                    this.addStandardBookmark(params);
                } else if (params.type == 2) {
                    Clipboard.putString(params.url);
                } 
            }
        });
    };

    obj.addStandardBookmark = function (options) {
        let bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]
            .getService(Ci.nsINavBookmarksService);
        let favsvc = Cc["@mozilla.org/browser/favicon-service;1"]
            .getService(Ci.nsIFaviconService);
        let ios = Cc["@mozilla.org/network/io-service;1"]
            .getService(Ci.nsIIOService);
        let uri = ios.newURI(options.url, null, null);
        let icon = ios.newURI("chrome://imacros/skin/imglog.png",
                              null, null);

        if (favsvc.setAndLoadFaviconForPage) {
            favsvc.setAndLoadFaviconForPage(
                uri, icon, true,
                Ci.nsIFaviconService.FAVICON_LOAD_NON_PRIVATE
            );
        } else {
            favsvc.setAndFetchFaviconForPage(uri, icon, true, null);
        }

        let bmid = bmsvc.insertBookmark(
            options.folderId, uri,
            bmsvc.DEFAULT_INDEX,
            options.title
        );
        
        
        let tagsvc = Cc["@mozilla.org/browser/tagging-service;1"]
            .getService(Ci.nsITaggingService);
        tagsvc.tagURI(uri, options.tags.split(","));
    };

    
    obj.openRecordPrefDlg = function () {
        window.openDialog('chrome://imacros/content/recordPrefDlg.xul','',
                          'modal,centerscreen');
        this.panel.updateControlPanel();
    };


    obj.editSelectedMacro = function() {
	let item = this.panel.selectedItem;
        if (!item || item.isContainer)
            return;
        let path = FIO.getMacroPath(item.path)
        let macro = {name: OS.Path.basename(path), path: path};
        this.edit(macro);
    };

    
    obj.edit = function(_macro, highlight_line) {
        let macro = _macro || this.currentMacro;
        if (!macro)
            return;
        let source_path = FIO.isFullPath(macro.path) ?
            macro.path : FIO.getMacroPath(macro.path);
        
        if (!Prefs.useExternalEditor) {
            
            var param = {name: macro.name,
                         path: source_path,
                         line: highlight_line};
            window.openDialog('chrome://imacros/content/edit.xul','',
                              'centerscreen,resizable,dialog=no,modal=no',
                              param);
            return;
        }

        try {
            var editor_path = Prefs.externalEditorPath;
            let process = Cc["@mozilla.org/process/util;1"]
                .createInstance(Ci.nsIProcess);

            let args = [];
            if (MiscUtils.isMacOSX() && /\.app$/.test(source_path)) {
                let bin_open = new FileUtils.File("/usr/bin/open");
                process.init(bin_open);
                args = ["-a", OS.Path.basename(editor_path).
                        replace(/\.app$/,''),
                        source_path];
            } else {
                process.init(new FileUtils.File(editor_path));
                args = [source_path];
            }

            
            process.runw(false, args, args.length);

        } catch (e) {
            Cu.reportError(e);
            this.panel.showErrorMessage(
                "Can not start editor located at: "+editor_path
            );
        }
        
    };


    obj.runMacro = function (data) {
        let runobject = data;
        if (!( "type" in runobject) || !("data" in runobject))
            return;

        if (this.playing)
            this.stop();

        let filename = null, code = null;
        let code_promise = null; 
        if ( runobject.type == "m" ) {
            filename  = OS.Path.normalize(runobject.data);
            let path = FIO.getMacroPath(filename);
            code_promise = OS.File.exists(path).then(exists => {
                if (!exists) {
                    iMacros.panel.showErrorMessage(
                        "iMacros run command: macro "+
                            filename+" not found"
                    );
                } else {
                    return FIO.readTextFile(path);
                }
            });
        } else if (runobject.type == "bookmarklet") {
            filename = "Embedded macro.iim";
            code_promise = Promise.resolve(runobject.data);
        } else if (runobject.type == "code") {
            let code = runobject.data.replace(/\s+/g, '+');
            code = StrUtils.decodeBase64(code);
            if (code.search(/iim(?:Play|Set|Display|Exit)\s*\([^\)]*\)/) != -1)
                filename = "Embedded code.js";
            else
                filename = "Embedded macro.iim";
            code_promise = Promise.resolve(code);
        } else {
            iMacros.panel.showErrorMessage(
                "iMacros run command:"+
                    " unknown parameter "+ type, ''
            );
            return;
        }

        return code_promise.then(code => {
            let param = {
                code: code,
                filename: filename,
                origin: runobject.origin,
                type: runobject.type,
                run: null
            };

            if (runobject.grant) {
                param.run = true;
            } else {
                window.openDialog('chrome://imacros/content/runwarning.xul',
                                  '', 'modal, centerscreen', param);
            }
            if (!param.run)
                return;

            if (param.filename.endsWith(".iim")) {
               return  iMacros.player.play(
                   {source: code, name: runobject.name}
               );
            } else if (param.filename.endsWith(".js")) {
               return iMacros.playjs(
                   {source: code, name: runobject.name}
               );
            }
            iMacros.panel.updateControlPanel();
        });
    };


    obj.share = function () {
        let item = this.panel.selectedItem;
        let path = FIO.getMacroPath(item.path);
        let name = OS.Path.basename(item.path);
        FIO.readTextFile(path).then( data => {
            let jsurl = this.makeBookmarkletURL(name, data);
            
            
            
            
            
            var params = { url: jsurl, name: name};
            window.openDialog('chrome://imacros/content/share.xul','',
                              'modal,centerscreen', params);
        }).catch(e => Cu.reportError(e));
    };

    
    obj.play = function() {
	let item = this.panel.selectedItem, macro;
        if (!item || item.isContainer)
            macro = "#Current.iim";
        else
            macro = item.path;
        let path = FIO.getMacroPath(macro);
        if (/\.js$/.test(macro)) {
            this.playjs({path: path});
        } else {
            this.player.play({path: path});
        }
        this.panel.updateControlPanel();
    };


    obj.playLoop = function() {
        let item = this.panel.selectedItem;
        if (!item || item.isContainer)
            return;
        
        if (item.path.match(/\.js$/i)) {
            this.panel.showErrorMessage(
                "The LOOP button can only be used with macro (\".iim\") files."+
                    "\nIn a Javascript (\".js\") file you can use"+
                    " Javascript itself for loops."
            );
            return;
        }
        let times = this.panel.maxLoopValue;
        let path = FIO.getMacroPath(item.path);
        this.player.play({path: path, times: times});
        this.panel.updateControlPanel();
    };


    obj.record = function() {
        this.recorder.start();
    };

    
    obj.saveMacroAs = function() { 
        let item = this.panel.selectedItem, macro;
        if (!item || item.isContainer)
            macro = "#Current.iim";
        else
            macro = item.path;
        let path = FIO.getMacroPath(macro);
        
        let param = {filename: OS.Path.basename(item.path),
                     folder: OS.Path.dirname(FIO.getMacroPath(item.path)),
                     confirm: false};
        window.openDialog('chrome://imacros/content/save.xul', '',
                          'modal,centerscreen', param);
        if (!param.confirm)
            return;

        let filename = param.filename;
        if (!/(?:\.iim|\.js)$/i.test(filename))
            filename = filename + ".iim";
        
        
        let re = new RegExp('\\s*[:*?|<>\\"/]+\\s*', "g");
        filename = filename.replace(re, "_");
        
        let new_dir = param.folder;
        let new_file_path = OS.Path.join(new_dir, filename);
        if (path == new_file_path)
            return;
        let macro_dir = Prefs.defaultMacroPath;
        const file_exists_msg = "File "+new_file_path+
              " already exists. Would you like to overwrite it?";
        return OS.File.exists(new_file_path).then(exists => {
            return exists? Dialogs.confirm(file_exists_msg) :
                Promise.resolve(true);
        }).then(proceed => {
            return proceed ? OS.File.copy(path, new_file_path) :
                Promise.reject(new Error("User cancelled operation"));
        }).then( () => {
            return param.bookmark ? FIO.readTextFile(new_file_path) : null;
        }).then(data => {
            if (data == null)
                return Promise.resolve();
            let mlink = "unavailable";
            if (new_file_path.startsWith(macro_dir)) {
                let endname = new_file_path.slice(macro_dir.length+1);
                mlink = "imacros://run/?m=" + encodeURIComponent(endname);
            }
            let code = encodeURIComponent(StrUtils.encodeBase64(data));
            let jsurl = this.makeBookmarkletURL(
                OS.Path.basename(new_file_path), data
            );
            let params = {
                title: OS.Path.basename(new_file_path),
                url: mlink,
                url2: "imacros://run/?code="+code,
                bookmarklet: jsurl,
                res: null,
                type: 1,
                folderId: -1
            };
            window.openDialog('chrome://imacros/content/bookmark.xul','',
                              'modal,centerscreen', params);
            if (params.res) {
                if (params.type == 1) {
                    this.addStandardBookmark(params);
                } else if (params.type == 2) {
                    Clipboard.putString(params.url);
                }
            }

            return Promise.resolve();
        }).then(() => {
            if (!new_file_path.startsWith(macro_dir)) {
                
                
                return;
            }
            
            let mtree = this.panel.sidebar.getMTreeObject();
            let name = new_file_path.slice(macro_dir.length+1);
            item = mtree.findItem(name);
            if (!item) {
                let p = name.split(FIO.pathSeparator);
                let con = mtree.children;
                for(let x = 0; x < p.length-1; x++) {
		    con = mtree.getContainer(p[x], con);
	        }
                mtree.insertLeaf(p[p.length-1], con);
                item = mtree.findItem(name);
            }
            let idx = mtree.getIndexOfItem(item);
            mtree.tree.view.selection.select(idx);
        }).catch(e => Cu.reportError(e));
    };

    
    obj.pause = function() {         
        if (!this.paused) {
            if (this.recording) {
                return this.recorder.pause().then(
                    () => iMacros.panel.updateControlPanel()
                );
            } else if (this.playing) {
                return this.player.pause().then(
                    () => iMacros.panel.updateControlPanel()
                );
            } else if (this.jsplaying) {
                return this.jsplayer2.pause().then(
                    () => iMacros.panel.updateControlPanel()
                );
            }
        } else {
            switch(this.paused) {
            case "recording":
                return this.recorder.unPause().then(
                    () => iMacros.panel.updateControlPanel()
                );
            case "playing":
                return this.player.unPause().then(
                    () => iMacros.panel.updateControlPanel()
                );
            case "jsplaying":
                return this.jsplayer2.unPause().then(
                    () => iMacros.panel.updateControlPanel()
                );
            }
        }
    };

    obj.stop = function() {    
        (this.paused ? this.pause() : Promise.resolve()).then(() => {
            if (this.playing) {
                this.player.stop();
            } else if (this.recording) {
                this.recorder.stop();
            }
            if (this.jsplaying) {
                this.jsplayer2.stop();
            }
            this.panel.showMacroTree();
        }).catch(e => Cu.reportError(e));
    };


    
    obj.savePageAs = function () {
        if (!this.recording) { 
            let cmd = document.getElementById("Browser:SavePage");
            cmd.doCommand("Browser:SavePage");
        } else {
            this.recorder.savePageAs();
        }
    };


    obj.takeScreenshot = function () {
        if (this.recording) {
            this.recorder.takeScreenshot();
        } else {    
            this.player.attachBrowser(gBrowser.selectedBrowser).then(() => {
                
                let name = this.player.deriveSaveAsName();
                let filename = name.endsWith(".png")? name : name+".png";
                return Dialogs.browseForFileSave(
                    "iMacros", filename,
                    Prefs.defaultDownloadPath
                );
            }).then(
                path => this.player.savePageAsImage(path, "png")
            ).then(() => this.player.detachBrowser());
        }
    };


    obj.clearCookies = function () {
        let confirm_promise = Promise.resolve();
        if (Prefs.warnOnClearCookies) {
            let check = {value: true};
            confirm_promise = Dialogs.confirmCheck(
                "Warning: ",
                "This will clear the global FF Browser cache. This includes deleting ALL COOKIES. Do you want to continue?" ,
                "Display this message next time again" ,
                check
            ).then(result => {
                if (!result)
                    return false;
                Prefs.warnOnClearCookies = check.value;
                return true;
            });
        }
        confirm_promise.then(yes => {
            if (yes) {
                Services.cookies.removeAll();
                try {
                    Services.cache2.clear();
                } catch(e) {
                    Cu.reportError(e);
                }
                if (this.recording) {
                    this.recorder.clearCookies();
                }
            }
        });
    };


    obj.addWaitCommand = function() {
        this.recorder.addWaitCommand();
    };


    obj.playjs = function(script) {
        let name = "", source_promise = null
        if (script.source) {
            source_promise = Promise.resolve(script.source);
            name = script.name || "Embedded code";
            this.currentMacro = {name: name, path: null};
        } else {
            source_promise = FIO.readTextFile(script.path);
            name = OS.Path.basename(script.path);
            this.currentMacro = {name: name, path: script.path};
        }
        return source_promise.then(code => {
            this.jssrc = code;
            if (!this.jssrc.length)
                throw new Error("Can not replay empty script");
            
            return this.jsplayer2.play(this.jssrc, name)

        }).catch(Cu.reportError);
    };


    obj.runLocalTest = function() {
        let skip_warning = Prefs.AlertFoxNoLocalTestWarning;
        let confirm_promise = Promise.resolve(true);
        if (!skip_warning) {
            let msg = "The AlertFox local test simulates server environment as close as possible."
            let check_msg = "Do not show this warning again";
            let check = {value: false};
            confirm_promise = Dialogs.confirmCheck(
                "iMacros", msg, check_msg, check
            ).then( result => {
                Prefs.AlertFoxNoLocalTestWarning = check.value;
                return result;
            });
        }
        confirm_promise.then(result => {
            if (!result)
                return;
            let path = FIO.getMacroPath(this.panel.selectedItem.path);
            if (path.endsWith(".js")) {
                this.panel.showErrorMessage(
                    "Local Test can only be used with macro (\".iim\") files."
                );
                return;
            } else {
                let AFTest = { iDrone: this.panel.sidebar.document.
                               getElementById("im-test-for-idrone").checked };
                this.player.play({path: path, name: OS.Path.basename(path)},
                                 AFTest);
            }
            this.panel.updateControlPanel();
        });
    };


    const wsdl_url = "https://my.alertfox.com/imu/AlertFoxManagementAPI.asmx";
    const AF_auth_host = "https://my.alertfox.com";
    obj.__uploadMacro = function(usr, pwd, skip) {
        let path = FIO.getMacroPath(this.panel.selectedItem.path);
        let btn = this.panel.sidebar.document.getElementById(
            skip ? "im-af-upload-button" : "im-online-test-button"
        );
        return FIO.readTextFile(path).then(code => {
            let args = {
                accountName: usr,
                accountPassword: pwd,
                macro: code,
                browserType: "FX",
                skipOnlineTest: skip
            };
            
            
            
            
            return SOAPClient.invoke(wsdl_url, "UploadMacro", args);
        }).then(rv => {
            btn.disabled = null;
            btn.image = "";
            if (!rv) {
                alert("Unexcpected error occured while uploading macro: "+
                      err.message);
                return;
            }
            if (rv.errorMessage) {
                alert(rv.errorMessage);
                return;
            } 
            if (!rv.UploadMacroResult.startsWith(AF_auth_host)) {
                alert("Unexpected server response. URL value "+
                      rv.UploadMacroResult+
                      " does not refer to AlertFox service.");
                return;
            }
            iMacros.addTab(rv.UploadMacroResult); 
        });
    };
    
    obj.uploadMacro = function(skipOnlineTest) {
        let logins = Services.logins.findLogins({}, AF_auth_host, "", null);
        if (logins.length == 0) {
            
            const msg = "No Alertfox credentials found. "+
                  "Please enter your credentials in the Settings dialog";
            Dialogs.confirm(msg).then(confirm => {
                if (confirm) {
                    this.showPrefDialog("cloud-service-pane");
                    setTimeout(() => iMacros.uploadMacro(), 0);
                }
            });
            return;
        }
        
        let uname = logins[0].username;
        let pwd = logins[0].password;
        if (logins.length > 1) {
            let stored_uname = Prefs.AlertFoxUserName;
            let login = logins.find((login, idx, arr) => {
                return login.username == stored_uname;
            });
            if (login) {
                uname = login.username;
                pwd = login.password;
            }
        }

        
        
        
        
        let args = {accountName: uname, accountPassword: pwd};
        let btn = this.panel.sidebar.document.
            getElementById(skipOnlineTest ? "im-af-upload-button" :
                           "im-online-test-button");
        btn.disabled = true;
        btn.image = "chrome://imacros/skin/waiting_16x16.gif";
        const msg = "Either user name or password is incorrect. "+
              "Please enter your credentials in the Settings dialog";
        SOAPClient.invoke(wsdl_url, "CheckLogin", args).then(rv => {
            if (rv.CheckLoginResult) {
                iMacros.__uploadMacro(uname, pwd, skipOnlineTest);
            } else {
                btn.image = ""; btn.disabled = null;
                Dialogs.confirm(msg).then(confirm => {
                    if (!confirm)
                        return;
                    
                    
                    iMacros.showPrefDialog("cloud-service-pane");
                    setTimeout(() => this.uploadMacro, 0);
                });
            }
        }).catch(e => {
            btn.image = ""; btn.disabled = null;
            alert("Error occured while checking credentials: "+
                  e.message);
            Cu.reportError(e);
        });
    };
    
    
    obj.observe = function (subject, topic, data) {
        
        if (topic == "imacros-delay-show") {
            if (subject != window)
                return;
            if (!(this.playing || this.recording)) {
                iMacros.panel.statLine2 = "";
                return;
            }
            this.panel.statLine2 = data;
        }
    };
    
    
    obj.topics = [
        "imacros-delay-show"
    ];

    
    obj.registerObservers = function() {
        this.topics.forEach(t => Services.obs.addObserver(this, t, false));
    };

    obj.unregisterObservers = function() {
        this.topics.forEach(t => Services.obs.removeObserver(this, t, false));
    };


    
    obj.generateWinId = function() {
        
        this.wid = window.btoa(crypto.getRandomValues(new Uint8Array(10)));
    };

    obj.getSample = function(url) {
        return new Promise(function(resolve, reject) {
            let name = url.split("/").pop();
            let xhr = new XMLHttpRequest();
            xhr.overrideMimeType("text/plain;charset=utf-8");
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4) {
                    if (xhr.status == 200) 
                        resolve({name: name, content: xhr.responseText});
                    else
                        reject(xhr.status);
                }
            }
            xhr.onerror = reject;
            xhr.open('GET', url, true);
            xhr.send(null);
        });
    };

    obj.copySampleMacros = function(ask) {
        let samples = [
            "ArchivePage.iim", "Download.iim", "Eval.iim", "Extract.iim",
            "ExtractAndFill.iim", "ExtractRelative.iim", "ExtractTable.iim",
            "ExtractURL.iim", "FillForm-XPath.iim", "FillForm.iim", 
            "Filter.iim", "Frame.iim", "Javascript-Dialogs.iim",
            "Loop-Csv-2-Web.iim", "Open6Tabs.iim", "SI-Get-Exchange-Rate.js",
            "SI-Run-Test.js", "SI-Send-Macro-Code.js", "SI-Test-Macro1.iim",
            "SI-Test-Macro2.iim", "SaveAs.iim", "SavePDF.iim",
            "SaveTargetAs.iim", "Self-Test.js", "SlideShow.iim",
            "Stopwatch.iim", "Tabs.iim", "TagPosition.iim",
            "TakeScreenshot-FX.iim", "Wsh-Extract-Rate.iim",
        ].map(f => "chrome://imacros/content/samples/Macros/"+f);

        let samples_promise = Promise.all(samples.map(this.getSample));
        let address_csv_promise = this.getSample(
            "chrome://imacros/content/samples/Datasources/Address.csv"
        );
        let profiler_xsl_promise = this.getSample(
            "chrome://imacros/content/samples/Profiler.xsl"
        );
        let base_dir = OS.Constants.Path.homeDir;
        if (MiscUtils.isWindows())
            base_dir = OS.Path.join(base_dir, "Documents");
        let samples_path = Prefs.defaultMacroPath;
        if (ask) {
            
            var param = {copy: false, path: null};
            window.openDialog("chrome://imacros/content/smplcopy.xul",
                              "", "modal,centerscreen", param);
            if (!param.copy)
                return;
            samples_path = param.path;
        } else {
            if (!samples_path) {
                samples_path = OS.Path.join(
                    base_dir, "iMacros", "Macros"
                );
                Prefs.defaultMacroPath = samples_path;
            }
            samples_path = OS.Path.join(samples_path, "Demo-Firefox");
        }
        
        let datasources_path = Prefs.defaultDataPath;
        if (!datasources_path) {
            datasources_path = OS.Path.join(
                base_dir, "iMacros", "Datasources"
            );
            Prefs.defaultDataPath = datasources_path;
        }
        
        let downloads_path = Prefs.defaultDownloadPath;
        if (!downloads_path) {
            downloads_path = OS.Path.join(
                base_dir, "iMacros", "Downloads"
            );
            Prefs.defaultDownloadPath = downloads_path;
        }

        return OS.File.makeDir(
            samples_path, {from: base_dir}
        ).then(() => OS.File.makeDir(
            datasources_path, {from: base_dir}
        )).then(() =>  OS.File.makeDir(
            downloads_path, {from: base_dir}
        )).then(() =>  samples_promise.then(
            macros => Promise.all(macros.map(
                macro => FIO.writeTextFile(
                    OS.Path.join(samples_path, macro.name),
                    macro.content
                )
            ))
        )).then(() => address_csv_promise.then(
            file => FIO.writeTextFile(
                OS.Path.join(datasources_path, file.name),
                file.content
            )
        )).then(() => profiler_xsl_promise.then(
            file => FIO.writeTextFile(
                OS.Path.join(downloads_path, file.name),
                file.content
            )
        )).then(() => {
            if (ask) { 
                let windows = Services.wm.getEnumerator("navigator:browser");
                while (windows.hasMoreElements()) {
                    let win = windows.getNext()
                        .QueryInterface(Ci.nsIDOMWindow);
                    win.setTimeout(() => {
                        win.iMacros.panel.updateMacroTree();
                    }, 1000);
                }
            }
        });
        
    };
        
    
    obj.onFirstTime = function() {
        this.copySampleMacros(false).then(() => {
            gBrowser.selectedTab = gBrowser.addTab(
                "http://www.iopus.com/imacros/home/fx/welcome.htm"
            );
        }).catch(Cu.reportError);
    };

    
    obj.onUpdate = function() {
        this.copySampleMacros(true).then(() => {
            gBrowser.selectedTab = gBrowser.addTab(
                "http://www.iopus.com/imacros/home/fx/quicktour/?v="+
                    imacros_version);
        }).catch(Cu.reportError);
    };
    
    obj.onLoad = function() {
        this.customizeUI();
        this.registerObservers();
        
        this.jssrc = "";
        this.in_iimPlay = false;
        if (Prefs.isJustInstalled) {
            Prefs.isJustInstalled = false
            this.onFirstTime()
        }
        else if (Prefs.isJustUpgraded) {
            Prefs.isJustUpgraded = false
            this.onUpdate()
        }
    };
    
    obj.customizeUI = function() {
        this.addiMacrosCSS();
        this.addToolbarButton();
        this.addiMacrosSidebarMenuItem();
        this.addiMacrosPanel();
    };

    obj.addiMacrosCSS = function() {
        let sss = Cc['@mozilla.org/content/style-sheet-service;1']
            .getService(Ci.nsIStyleSheetService);
        sss.loadAndRegisterSheet(
            Services.io.newURI("chrome://imacros/skin/imacros.css", null, null),
            sss.AUTHOR_SHEET
        );
    };

    obj.addToolbarButton = function() {
        if (MiscUtils.toolbarButtonAdded)
            return;
        MiscUtils.toolbarButtonAdded = true;
        
        CustomizableUI.createWidget({
            id: 'imacros-toggle-button',
            defaultArea: CustomizableUI.AREA_NAVBAR,
            label: 'iMacros for Firefox',
            tooltiptext: 'Open iMacros Sidebar',
            onCommand(evt) {
                
                let browser_window = evt.target.ownerDocument.defaultView;
                browser_window.SidebarUI.toggle("imacros_ControlPanel");
            }
        });
    };

    obj.createElement = function(doc, tag_name, atts) {
        let el = doc.createElement(tag_name);
        for (let a in atts) 
            el.setAttribute(a, atts[a]);
        return el;
    };

    obj.addiMacrosSidebarMenuItem = function() {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        let doc = document;
        let viewSidebarMenu = doc.getElementById("viewSidebarMenu");
        let menuitem = this.createElement(
            doc, "menuitem", {
                "id": "imacros_key_OpenPanel_id",
                "key": "imacros_key_OpenPanel",
                "label": "iMacros for Firefox",
                "observes": "imacros_ControlPanel"
            }
        );
        viewSidebarMenu.appendChild(menuitem);
        let mainKeyset = doc.getElementById("mainKeyset");
        let key = this.createElement(
            doc, "key", {"id": "imacros_key_OpenPanel",
                         "command": "imacros_ControlPanel"}
        );
        
        if (Prefs.useToggleHotkey) {
            key.setAttribute("keycode", Prefs.openSidebarKbdShortcut);
        }
        mainKeyset.appendChild(key);
        let mainBroadcasterSet = doc.getElementById("mainBroadcasterSet");
        let broadcaster = this.createElement(
            doc, "broadcaster",
            {"id": "imacros_ControlPanel", "autoCheck": "false",
             "label": "iMacros for Firefox", "type": "checkbox",
             "group": "sidebar", "sidebartitle": "iMacros",
             "sidebarurl": "chrome://imacros/content/iMacrosSidebar.xul",
             "oncommand": "SidebarUI.toggle('imacros_ControlPanel')"}
        );
        mainBroadcasterSet.appendChild(broadcaster);
    };
    
    obj.addiMacrosPanel = function() {
        let doc = document;
        
        
        
        
        
        
        
        
        
        
        let panel = this.createElement(
            doc, "panel", {"id": "imacros-info-panel", "noautohide": "true"}
        );
        
        
        let status_container = this.createElement(doc, "box", {
            "id": "imacros-info-panel",
            "onclick": "iMacros.panel.onPanelClick()"
        });
        panel.appendChild(status_container);
        
        let hbox = this.createElement(doc, "hbox", {});
        status_container.appendChild(hbox);
        
        hbox.appendChild(this.createElement(doc, "box", {
            "id": "imacros-info-panel-logo"
        }));
        
        let flex_box = this.createElement(doc, "box", {"flex": "1"});
        hbox.appendChild(flex_box);
        
        let vbox = this.createElement(doc, "vbox", {});
        flex_box.appendChild(vbox);
        
        
        vbox.appendChild(this.createElement(doc, "label", {
            "id": "imacros-info-panel-status3"
        }));
        
        
        vbox.appendChild(this.createElement(doc, "label", {
            "id": "imacros-info-panel-status2"
        }));
        
        
        
        

        
        let button_box = this.createElement(doc, "hbox", {
            "id": "imacros-info-panel-button-box", "flex": "1"
        });
        panel.appendChild(button_box);
        
        button_box.appendChild(
            this.createElement(doc, "spacer", {"flex": "1"})
        );
        
        hbox = this.createElement(doc, "hbox", {"pack": "center"});
        button_box.appendChild(hbox);
        
        
        
        
        hbox.appendChild(this.createElement(doc, "toolbarbutton", {
            id: "imacros-info-panel-stop-button", flex: "1",
            label: "Stop",
            oncommand: "iMacros.stop()"
        }));
        
        
        
        
        
        hbox.appendChild(this.createElement(doc, "toolbarbutton", {
            id: "imacros-info-panel-pause-button", flex: "1",
            label: "Pause",
            oncommand: "iMacros.pause()"
        }));
        
        
        
        
        hbox.appendChild(this.createElement(doc, "toolbarbutton", {
            id: "imacros-info-panel-play-button", flex: "1",
            label: "Play",
            oncommand: "iMacros.play()"
        }));
        
        
        
        
        hbox.appendChild(this.createElement(doc, "toolbarbutton", {
            id: "imacros-info-panel-record-button", flex: "1",
            label: "Record",
            oncommand: "iMacros.record()"
        }));
        
        
        button_box.appendChild(
            this.createElement(doc, "spacer", {"flex": "1"})
        );
        

        
        panel.appendChild(this.createElement(doc, "box", {
            "id": "imacros-info-panel-indicator"
        }));
        
        
        let vbox2 = this.createElement(doc, "vbox", {
            id: "imacros-message-box-container",
            collapsed: "true",
            flex: "1"
        });
        panel.appendChild(vbox2);
        
        let box = this.createElement(doc, "box", {
            id: "imacros-messages", flex: "1"
        });
        vbox2.appendChild(box);
        
        
        
        
        
        
        box.appendChild(this.createElement(doc, "textbox", {
            "id": "imacros-message-box", "flex": "1", "value": "",
            "class": "plain", "readonly": "true",
            "msgtype": "message", "multiline": "true"
        }));
        
        
        let hbox2 = this.createElement(doc, "hbox", {
            id: "imacros-message-box-buttons-container"
        });
        vbox2.appendChild(hbox2);
        
        
        hbox2.appendChild(this.createElement(doc, "toolbarbutton", {
            id: "imacros-message-box-button-edit",
            oncommand: "iMacros.panel.mboxEdit()"
        }));
        
        
        hbox2.appendChild(this.createElement(doc, "toolbarbutton", {
            id: "imacros-message-box-button-help",
            oncommand: "iMacros.panel.mboxHelp()"
        }));
        
        
        hbox2.appendChild(this.createElement(doc, "toolbarbutton", {
            id: "imacros-message-box-button-close",
            oncommand: "iMacros.panel.mboxClose()"
        }));
        
        
        
        doc.getElementById("main-window").appendChild(panel);
        
        
        
    };

    obj.onUnload = function() {
        this.unregisterObservers();
    };

    obj.terminate = function() {
        
        let doc = document;
        SidebarUI.hide("imacros_ControlPanel");
        doc.getElementById("main-window").removeChild(
            doc.getElementById("imacros-info-panel")
        );
        doc.getElementById("viewSidebarMenu").removeChild(
            doc.getElementById("imacros_key_OpenPanel_id")
        );
        doc.getElementById("mainKeyset").removeChild(
            doc.getElementById("imacros_key_OpenPanel")

        );
        doc.getElementById("mainBroadcasterSet").removeChild(
            doc.getElementById("imacros_ControlPanel")
        );
        CustomizableUI.destroyWidget('imacros-toggle-button');
        MiscUtils.toolbarButtonAdded = false;
        
        let sss = Cc['@mozilla.org/content/style-sheet-service;1']
            .getService(Ci.nsIStyleSheetService);
        sss.unregisterSheet(
            Services.io.newURI("chrome://imacros/skin/imacros.css", null, null),
            sss.AUTHOR_SHEET
        );
        
        this.unregisterObservers();
        let p = Promise.resolve();
        if (this.recording)
            p = p.then(() => this.recorder.stop());
        p = p.then(() => this.player.onUnload());
        
        return p;
    };

    return obj;

}) ();


window.addEventListener("unload", function() {
    iMacros.onUnload();
}, false);

iMacros.generateWinId();
setTimeout(() => iMacros.onLoad(), 0);
